﻿//Copyright (c) 2010 Christopher E. S. King (http://chriskingconsulting.com)
//
//Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files 
// (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, 
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do 
// so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE 
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

using System;
using System.Collections.Generic;
using System.Collections;
using System.Linq;
using System.Data.Linq;
using System.Data.Linq.Mapping;
using System.Text.RegularExpressions;
using System.Linq.Expressions;
using System.Reflection;
using System.Data.Common;
using System.Reflection.Emit;
using System.Diagnostics;
using System.Text;
using System.Runtime.CompilerServices;

using King.Extensions;
using King.Reflection;

namespace King.Linq {

    #region IKingQueryable             (IKingQueryable, IKingQueryProvider, IKingOrderedQueryable)
    public static class Extensions {

        public static string ToSql(this IKingQueryable source) {
            return source.GetQuery<DbCommand>().GetSql();
        }

        public static IEnumerable<Expression> GetExpressions(this Expression source) {
            if (source is MethodCallExpression) return ((MethodCallExpression)source).GetExpressions();
            if (source is LambdaExpression) return ((LambdaExpression)source).GetExpressions();
            if (source is MemberExpression) return ((MemberExpression)source).GetExpressions();
            if (source is ConstantExpression) return ((ConstantExpression)source).GetExpressions();
            if (source is NewExpression) return ((NewExpression)source).GetExpressions();
            if (source is BinaryExpression) return ((BinaryExpression)source).GetExpressions();
            if (source is UnaryExpression) return ((UnaryExpression)source).GetExpressions();
            if (source is ParameterExpression) return ((ParameterExpression)source).GetExpressions();
            if (source is NewArrayExpression) return ((NewArrayExpression)source).GetExpressions();
            if (source is TypeBinaryExpression) return ((TypeBinaryExpression)source).GetExpressions();
            if (source is InvocationExpression) return ((InvocationExpression)source).GetExpressions();
            if (source is ConditionalExpression) return ((ConditionalExpression)source).GetExpressions();
            throw new NotImplementedException();
        }
        public static IEnumerable<Expression> GetExpressions(this ConditionalExpression source) {
            yield return source.Test;
            yield return source.IfTrue;
            yield return source.IfFalse;
        }
        public static IEnumerable<Expression> GetExpressions(this InvocationExpression source) {
            yield return source.Expression;
            foreach (var argument in source.Arguments)
                yield return argument;
        }
        public static IEnumerable<Expression> GetExpressions(this MethodCallExpression source) {
            if (source.Method.IsStatic)
                yield return source.Object;
            foreach (var argument in source.Arguments)
                yield return argument;
        }
        public static IEnumerable<Expression> GetExpressions(this LambdaExpression source) {
            yield return source.Body;
            foreach (var parameter in source.Parameters)
                yield return parameter;
        }
        public static IEnumerable<Expression> GetExpressions(this MemberExpression source) {
            yield return source.Expression;
        }
        public static IEnumerable<Expression> GetExpressions(this ConstantExpression source) {
            yield break;
        }
        public static IEnumerable<Expression> GetExpressions(this NewExpression source) {
            foreach (var argument in source.Arguments)
                yield return argument;
        }
        public static IEnumerable<Expression> GetExpressions(this BinaryExpression source) {
            yield return source.Left;
            yield return source.Right;
        }
        public static IEnumerable<Expression> GetExpressions(this UnaryExpression source) {
            yield return source.Operand;
        }
        public static IEnumerable<Expression> GetExpressions(this ParameterExpression source) {
            yield break;
        }
        public static IEnumerable<Expression> GetExpressions(this NewArrayExpression source) {
            foreach (var expression in source.Expressions)
                yield return expression;
        }
        public static IEnumerable<Expression> GetExpressions(this TypeBinaryExpression source) {
            yield return source.Expression;
        }
        public static string Print(this Expression expression) {

            var expressionType = expression.NodeType;

            if (expression == null)
                return "null";

            if (expression is MethodCallExpression) {
                var method = ((MethodCallExpression)expression).Method;
                return "{0} {1}{2}: {3}".Substitute(
                    expressionType,
                    method.Name,
                    method.GetGenericArguments().StringJoin("<", ", ", o => o.Name, ">"),
                    expression);
            }

            if (expression is ConstantExpression)
                return "{0}(const): {1}".Substitute(expressionType, expression);

            return "{0}: {1}".Substitute(expressionType, expression);
        }
        public static string PrintTree(this IQueryable queryable, params string[] header) {
            return queryable.Expression.PrintTree(header);
        }
        public static string PrintTree(this Expression expression, params string[] header) {
            var sb = new StringBuilder();

            foreach (var o in header)
                sb.AppendLine(o);

            int order = 1;

            var postOrder = TreeTraverse.PostOrder(
                expression,
                o => o.GetExpressions().Where(p => p != null && p.NodeType != ExpressionType.Parameter),
                (o, d) => new { Node = o, Order = order++ }).ToArray();

            var ordering = postOrder
                .ToDictionary(o => o.Node, o => o.Order);

            var preOrder = TreeTraverse.PreOrder(
                expression,
                o => o.GetExpressions().Where(p => p != null && p.NodeType != ExpressionType.Parameter),
                (o, d) => new { Node = o, Depth = d }).ToArray();

            foreach (var o in preOrder) {

                var indent = "".Indent(o.Depth, 3);
                var nodeToString = o.Node.ToString();
                order = 0;
                ordering.TryGetValue(o.Node, out order);

                nodeToString = nodeToString.Replace("King.Linq.", "");
                nodeToString = Regex.Replace(nodeToString, @"<>f__AnonymousType(?<id>\d)`(?<count>\d)",
                    delegate(Match match) {
                        var id = int.Parse(match.Groups["id"].Value);
                        var count = int.Parse(match.Groups["count"].Value);
                        return "a" + id + "`" + count;
                    });
                nodeToString = Regex.Replace(nodeToString, @"value\([\w\d\.\+]*<>c__DisplayClass(?<id>\d)\)",
                    delegate(Match match) {
                        var id = int.Parse(match.Groups["id"].Value);
                        return "l" + id;
                    });
                nodeToString = Regex.Replace(nodeToString, @"<>h__TransparentIdentifier(?<id>\d)",
                    delegate(Match match) {
                        var id = int.Parse(match.Groups["id"].Value);
                        return "v" + id;
                    });

                sb.AppendLine("{0}{1}:{2}", indent, order, nodeToString);
            }

            sb.AppendLine();

            return sb.ToString();
        }
    }

    public static class PredicateBuilder {
        public static Expression Or(Expression e) {
            return Or(e.Type.GetEnumerationElementType(), (IEnumerable)((ConstantExpression)e).Value);
        }
        public static Expression And(Expression e) {
            return And(e.Type.GetEnumerationElementType(), (IEnumerable)((ConstantExpression)e).Value);
        }
        public static Expression Or(Type type, IEnumerable e) {
            var lambda = typeof(Func<,>).MakeGenericType(type, typeof(bool));
            var t = Expression.Parameter(type, "t");

            return e.Cast<object>().Aggregate(False(type),
                (a, o) => Or(type, a, Expression.Lambda(lambda, Expression.Equal(t, Expression.Constant(o)), t)));
        }
        public static Expression And(Type type, IEnumerable e) {
            var lambda = typeof(Func<,>).MakeGenericType(type, typeof(bool));
            var t = Expression.Parameter(type, "t");

            return e.Cast<object>().Aggregate(True(type),
                (a, o) => And(type, a, Expression.Lambda(lambda, Expression.Equal(t, Expression.Constant(o)), t)));
        }
        public static Expression<Func<T, bool>> Or<T>(IEnumerable<T> e) {
            return e.Cast<T>().Aggregate(False<T>(), (a, o) => a.Or(t => t.Equals(o)));
        }
        public static Expression<Func<T, bool>> And<T>(IEnumerable<T> e) {
            return e.Cast<T>().Aggregate(True<T>(), (a, o) => a.And(t => t.Equals(o)));
        }
        public static LambdaExpression True(Type type) {
            var lambda = typeof(Func<,>).MakeGenericType(type, typeof(bool));
            var t = Expression.Parameter(type, "t");
            return Expression.Lambda(lambda, Expression.Constant(true), t);
        }
        public static LambdaExpression False(Type type) {
            var lambda = typeof(Func<,>).MakeGenericType(type, typeof(bool));
            var t = Expression.Parameter(type, "t");
            return Expression.Lambda(lambda, Expression.Constant(false), t);
        }
        public static Expression<Func<T, bool>> True<T>() { return t => true; }
        public static Expression<Func<T, bool>> False<T>() { return t => false; }
        public static Expression<Func<T, bool>> Or<T>(
            this Expression<Func<T, bool>> lhs,
            Expression<Func<T, bool>> rhs) {

            return (Expression<Func<T, bool>>)Chain(typeof(T), ExpressionType.OrElse, lhs, rhs);
        }
        public static Expression<Func<T, bool>> And<T>(
            this Expression<Func<T, bool>> lhs,
            Expression<Func<T, bool>> rhs) {

            return (Expression<Func<T, bool>>)Chain(typeof(T), ExpressionType.AndAlso, lhs, rhs);
        }
        public static LambdaExpression Or(Type type, LambdaExpression lhs, LambdaExpression rhs) {
            return Chain(type, ExpressionType.OrElse, lhs, rhs);
        }
        public static LambdaExpression And(Type type, LambdaExpression lhs, LambdaExpression rhs) {
            return Chain(type, ExpressionType.AndAlso, lhs, rhs);
        }
        private static LambdaExpression Chain(
            Type type,
            ExpressionType binaryExpressionType,
            LambdaExpression lhs,
            LambdaExpression rhs) {

            var lambda = typeof(Func<,>).MakeGenericType(type, typeof(bool));
            var left = lhs.Body;
            var right = Expression.Invoke(rhs, lhs.Parameters.Cast<Expression>());
            var chain = Expression.MakeBinary(binaryExpressionType, left, right);
            return Expression.Lambda(lambda, chain, lhs.Parameters);
        }
    }

    public interface IKingQueryProvider : IQueryProvider {
        new IKingQueryable CreateQuery(Expression expression);
        new IKingQueryable<T> CreateQuery<T>(Expression expression);
        object Execute(IKingQueryable queryable);
        T Execute<T>(IKingQueryable queryable);
        object GetQuery(IKingQueryable queryable);
        S GetQuery<S>(IKingQueryable queryable);
    }

    public interface IKingQueryable : IEnumerable, IQueryable {
        new Type ElementType { get; }
        new Expression Expression { get; }
        new IKingQueryProvider Provider { get; }
        IQueryable ToQueryable();
        object GetQuery();
        S GetQuery<S>();
        TimeSpan QueryTime { get; }
        object Execute();
        bool Executed { get; }
        bool AllowManyExecution { get; set; }
        string Comment { get; set; }
    }
    public interface IKingQueryable<T> : IKingQueryable, IQueryable<T>, IEnumerable<T> {
        new IQueryable<T> ToQueryable();
        new T Execute();
    }

    public interface IKingOrderedQueryable : IKingQueryable, IOrderedQueryable { }
    public interface IKingOrderedQueryable<T> : IKingQueryable<T>, IKingOrderedQueryable, IOrderedQueryable<T> { }
    #endregion

    #region IKingQueryable             (KingQueryable, KingQueryProvider)
    public static partial class KingQueryable {

        #region Static Public members
        public static event OnExecuteDelegate OnExecute {
            add { lock (s_onExecuteLock) s_onExecute += value; }
            remove { lock (s_onExecuteLock) s_onExecute -= value; }
        }
        #endregion

        #region Static Internal Members
        internal static void RaiseOnExecute(IKingQueryable KingQueryable) {
            IEnumerable<OnExecuteDelegate> subscribers;
            lock (s_onExecute) {
                if (s_onExecute == null)
                    return;
                subscribers = s_onExecute.GetInvocationList().Cast<OnExecuteDelegate>();
            }

            foreach (var subscriber in subscribers)
                subscriber(KingQueryable);
        }
        #endregion

        #region Private Static Members
        private static Type Transform(Type type) {

            if (type == typeof(IKingQueryable))
                return typeof(IEnumerable);

            if (!type.IsGenericType)
                return type;

            var genericArguments = type.GetGenericArguments().Select(o => Transform(o)).ToArray();
            var genericTypeDefinition = type.GetGenericTypeDefinition();

            if (genericTypeDefinition == typeof(Expression<>))
                return Transform(genericArguments[0]);

            if (genericTypeDefinition == typeof(IKingQueryable<>))
                return typeof(IEnumerable<>).MakeGenericType(genericArguments[0]);

            return genericTypeDefinition.MakeGenericType(genericArguments);
        }
        private static Expression GetExpression(object argument) {

            if (argument is Expression)
                return (Expression)argument;

            if (argument is IKingQueryable)
                return ((IKingQueryable)argument).Expression;

            if (argument is IEnumerable) {
                return Expression.Constant(argument,
                   (from o in argument.GetType().GetInterfaces()
                    where o.IsInstantiationOf(typeof(IEnumerable<>))
                    select o).FirstOrDefault() ?? typeof(IEnumerable));
            }

            return ConstantExpression.Constant(argument);
        }
        private static T Invoke<T, TQuery>(
            MethodInfo methodInfo,
            params object[] arguments) {

            var provider = ((IKingQueryable)arguments[0]).Provider;
            var call = Expression.Call(null, methodInfo, arguments.Select(o => GetExpression(o)));
            return (T)provider.CreateQuery<TQuery>(call);
        }
        private static T Invoke<T>(
            MethodInfo methodInfo,
            params object[] arguments) {

            var provider = ((IKingQueryable)arguments[0]).Provider;
            var call = Expression.Call(null, methodInfo, arguments.Select(o => GetExpression(o)));
            return provider.CreateQuery<T>(call).Execute();
        }
        #endregion

        #region Static Private Data members
        private static object s_onExecuteLock = new object();
        private static event OnExecuteDelegate s_onExecute;
        #endregion
    }

    public class KingTable {

        #region Private Data Members
        private ITable m_table;
        #endregion

        #region Constructor
        internal KingTable(ITable table) {
            m_table = table;
        }
        #endregion

        #region Public Members
        public ITable Table {
            get { return m_table; }
        }
        #endregion
    }
    public class KingTable<T> : KingTable, IKingQueryable<T> where T : class {

        #region Private Data Members
        private Table<T> m_table;
        private KingQueryable<T> m_KingQueryable;
        #endregion

        #region Constructor
        public KingTable(Table<T> table, Func<IKingQueryable, object> getQuery)
            : base(table) {
            m_table = table;
            m_KingQueryable =
                new KingQueryable<T>(
                    new KingQueryProvider(m_table, getQuery),
                    Expression.Constant(this));
        }
        #endregion

        #region IEnumerable<T> Members
        public IEnumerator<T> GetEnumerator() {
            return m_KingQueryable.GetEnumerator();
        }
        IEnumerator IEnumerable.GetEnumerator() {
            return m_KingQueryable.GetEnumerator();
        }
        #endregion

        #region IKingQueryable Members
        public Type ElementType {
            get { return m_KingQueryable.ElementType; }
        }
        public Expression Expression {
            get { return m_KingQueryable.Expression; }
        }
        public IKingQueryProvider Provider {
            get { return m_KingQueryable.Provider; }
        }
        IQueryable IKingQueryable.ToQueryable() {
            return ToQueryable();
        }
        public IQueryable<T> ToQueryable() {
            return m_table;
        }
        object IKingQueryable.Execute() {
            return ((IKingQueryable)m_KingQueryable).Execute();
        }
        public T Execute() {
            return m_KingQueryable.Execute();
        }
        public object GetQuery() {
            return Provider.GetQuery(this);
        }
        public S GetQuery<S>() {
            return Provider.GetQuery<S>(this);
        }
        public TimeSpan QueryTime {
            get { return m_KingQueryable.QueryTime; }
        }
        public bool Executed {
            get { return m_KingQueryable.Executed; }
        }
        public bool AllowManyExecution {
            get { return m_KingQueryable.AllowManyExecution; }
            set { m_KingQueryable.AllowManyExecution = value; }
        }
        public string Comment {
            get { return m_KingQueryable.Comment; }
            set { m_KingQueryable.Comment = value; }
        }
        #endregion

        #region IQueryable Members
        Type IQueryable.ElementType { get { return ((IQueryable)m_KingQueryable).ElementType; } }
        Expression IQueryable.Expression { get { return ((IQueryable)m_KingQueryable).Expression; } }
        IQueryProvider IQueryable.Provider { get { return ((IQueryable)m_KingQueryable).Provider; } }
        #endregion

        #region Public Members
        public new Table<T> Table { get { return m_table; } }
        public void InsertOnSubmit(T entity) {
            m_table.InsertOnSubmit(entity);
        }
        public void InsertAllOnSubmit(IEnumerable<T> entities) {
            m_table.InsertAllOnSubmit(entities);
        }
        public void DeleteOnSubmit(T entity) {
            m_table.DeleteOnSubmit(entity);
        }
        public void DeleteAllOnSubmit(IEnumerable<T> entities) {
            m_table.DeleteAllOnSubmit(entities);
        }
        #endregion
    }

    internal class KingQueryProvider : IKingQueryProvider {

        #region Private Data Members
        private IQueryProvider m_provider;
        private Func<IKingQueryable, object> m_getQuery;
        #endregion

        #region Constructor
        internal KingQueryProvider(IQueryProvider provider, Func<IKingQueryable, object> getQuery) {
            m_provider = provider;
            m_getQuery = getQuery;
        }
        #endregion

        #region Private Members
        private IEnumerable<T> GetEnumerator<T>() {
            throw new NotImplementedException();
        }
        #endregion

        #region Internal Members
        internal IQueryable<T> ToQueryable<T>(KingQueryable<T> KingQueryable) {
            IQueryable<T> queryable = KingQueryable;
            return m_provider.CreateQuery<T>(queryable.Expression);
        }
        #endregion

        #region IKingQueryProvider Members
        public IKingQueryable<T> CreateQuery<T>(Expression expression) {
            return new KingQueryable<T>(this, expression);
        }
        public IKingQueryable CreateQuery(Expression expression) {
            var type = expression.Type;

            if (!type.IsGenericType || type.GetGenericTypeDefinition() != typeof(IKingQueryable<>))
                throw new ArgumentException();

            return (IKingQueryable)typeof(KingQueryable<>)
                .MakeGenericType(type.GetGenericArguments()[0])
                .Activate(this, expression);
        }
        public object Execute(IKingQueryable queryable) {
            return Execute(queryable);
        }
        public T Execute<T>(IKingQueryable queryable) {
            // #execute
            KingQueryable.RaiseOnExecute(queryable);
            var result = m_provider.Execute(((IQueryable)queryable).Expression);
            return (T)result;
        }
        public object GetQuery(IKingQueryable queryable) {
            return m_getQuery(queryable);
        }
        public S GetQuery<S>(IKingQueryable queryable) {
            return (S)GetQuery(queryable);
        }
        #endregion

        #region IQueryProvider Members
        IQueryable<T> IQueryProvider.CreateQuery<T>(Expression expression) {
            throw new InvalidOperationException();
        }
        IQueryable IQueryProvider.CreateQuery(Expression expression) {
            throw new InvalidOperationException();
        }
        object IQueryProvider.Execute(Expression expression) {
            throw new InvalidOperationException();
        }
        T IQueryProvider.Execute<T>(Expression expression) {
            throw new InvalidOperationException();
        }
        #endregion

    }
    internal class KingQueryable<T> : IKingQueryable<T>, IKingOrderedQueryable<T> {

        #region Private Data Members
        private IKingQueryProvider m_provider;
        private Expression m_expression;
        private Expression m_linqExpression;
        private TimeSpan? m_queryTime;
        private bool m_allowManyExecution;
        private string m_comment;
        #endregion

        #region Constructor
        internal KingQueryable(IKingQueryProvider provider, Expression expression) {
            m_expression = expression;
            m_provider = provider;
        }
        #endregion

        #region Private Members
        private S Execute<S>() {
            //if (!Debugger.IsAttached && Executed && !m_allowManyExecution)
            //    throw new ArgumentException();

            var start = DateTime.Now;
            var result = Provider.Execute<S>(this);
            m_queryTime = DateTime.Now - start;

            return result;
        }
        #endregion

        #region IEnumerable<T> Members
        public IEnumerator<T> GetEnumerator() {
            return Execute<IEnumerable<T>>().GetEnumerator();
        }
        IEnumerator IEnumerable.GetEnumerator() {
            return GetEnumerator();
        }
        #endregion

        #region IKingQueryable Members
        public Type ElementType {
            get { return m_expression.Type; }
        }
        public Expression Expression {
            get { return m_expression; }
        }
        public IKingQueryProvider Provider {
            get { return m_provider; }
        }
        IQueryable IKingQueryable.ToQueryable() {
            return ToQueryable();
        }
        public IQueryable<T> ToQueryable() {
            return ((KingQueryProvider)m_provider).ToQueryable(this);
        }
        object IKingQueryable.Execute() {
            return Execute();
        }
        public T Execute() {
            return Execute<T>();
        }
        public object GetQuery() {
            return Provider.GetQuery(this);
        }
        public S GetQuery<S>() {
            return m_provider.GetQuery<S>(this);
        }
        public string Comment {
            get { return m_comment; }
            set { m_comment = value; }
        }
        #endregion

        #region IQueryable Members
        Type IQueryable.ElementType { get { return Expression.Type; } }
        Expression IQueryable.Expression {
            get {
                // #transform
                if (m_linqExpression == null)
                    m_linqExpression = KingExpressionTransform.ToLinqExpression(m_expression);
                return m_linqExpression;
            }
        }
        IQueryProvider IQueryable.Provider {
            // Did you inclue King.Linq? 
            get { throw new InvalidOperationException(); }
        }
        public TimeSpan QueryTime {
            get {
                if (!Executed)
                    throw new InvalidOperationException();
                return m_queryTime.Value;
            }
        }
        public bool Executed {
            get { return m_queryTime != null; }
        }
        public bool AllowManyExecution {
            get { return m_allowManyExecution; }
            set { m_allowManyExecution = value; }
        }
        #endregion

        #region Object Overrides
        public override string ToString() {
            if (this.GetQuery() is DbCommand)
                return this.ToSql();
            return base.ToString();
        }
        #endregion
    }

    [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.Method)]
    public class OrderAttribute : Attribute {
        private int m_order;
        public OrderAttribute(int order) { m_order = order; }
        internal int Order { get { return m_order; } }
    }

    internal class KingExpressionTransform {

        #region Definitions
        private class Frame {

            #region Private Data Members
            private object m_this;
            private Expression m_thisExpression;
            private Dictionary<string, Expression> m_parameterExpressions;
            private Expression m_expression;
            #endregion

            #region Constructor
            internal Frame(MethodCallExpression e) {
                var method = e.Method;
                m_this = method.IsStatic ? null : method.DeclaringType.Activate();
                var defaultArguments = method.GetParameters().Select(o => o.ParameterType.GetDefaultValue());
                var queryable = method.Invoke<IKingQueryable>(m_this, defaultArguments);
                m_expression = queryable.Expression;

                m_thisExpression = e.Object;
                m_parameterExpressions = new Dictionary<string, Expression>();
            }
            #endregion

            #region Internal Members
            internal Expression Expression { get { return m_expression; } }
            internal object This { get { return m_this; } }
            internal Expression ThisExpression { get { return m_thisExpression; } }
            internal Expression GetParameter(string parameterName) {
                throw new NotImplementedException();
                //return m_parameterExpressions[parameterName];
            }
            #endregion
        }
        private class AnonymousType {

            #region Private Data Members
            private IEnumerable<Type> m_types;
            private IEnumerable<string> m_names;
            #endregion

            #region Constructor
            internal AnonymousType(IEnumerable<MemberInfo> memberInfos) {
                m_names = memberInfos.Select(o => o.Name).ToArray();
                var types = new List<Type>();
                foreach (var member in memberInfos) {
                    if (member is PropertyInfo) types.Add(member.GetPropertyOrFieldType());
                    if (member is FieldInfo) types.Add(member.GetPropertyOrFieldType());
                    if (member is MethodInfo) types.Add(((MethodInfo)member).ReturnType);
                }
                m_types = types;
            }
            internal AnonymousType(IEnumerable<Type> types, IEnumerable<string> names) {
                m_types = types;
                m_names = names;
            }
            #endregion

            #region Internal Members
            internal ConstructorInfo Constructor { get; set; }
            internal IEnumerable<MemberInfo> Members { get; set; }
            #endregion

            #region Object Overrides
            public override bool Equals(object obj) {
                var rhs = obj as AnonymousType;
                if (rhs == null) return false;

                if (!m_types.AreEqual(rhs.m_types))
                    return false;

                if (!m_names.AreEqual(rhs.m_names))
                    return false;

                return true;
            }
            public override int GetHashCode() {
                return m_types.Aggregate(0, (a, o) => a ^ o.GetHashCode());
            }
            #endregion
        }
        #endregion

        #region Static Internal Members
        internal static Expression ToLinqExpression(Expression expression) {
            var state = new KingExpressionTransform();
            return state.Transform(expression);
        }
        #endregion

        #region Static Private Members
        private static T Transform<T>(MemberInfo memberInfo) where T : MemberInfo {
            return (T)Transform(memberInfo);
        }
        private static MemberInfo Transform(MemberInfo memberInfo) {
            var methodInfo = memberInfo as MethodInfo;
            if (methodInfo != null && methodInfo.IsStatic) {
                if (!methodInfo.IsGenericMethod)
                    return methodInfo;

                var genericArguments = methodInfo.GetGenericArguments().Select(o => Transform(o)).ToArray();
                var genericMethodDefinition = methodInfo.GetGenericMethodDefinition();
                MethodInfo linqMethod;
                if (s_methodTransforms.TryGetValue(genericMethodDefinition, out linqMethod))
                    genericMethodDefinition = linqMethod;
                return genericMethodDefinition.MakeGenericMethod(genericArguments);
            }

            var declaringType = Transform(memberInfo.DeclaringType);
            var memberInfos = declaringType.Get<MemberInfo>(memberInfo.Name);

            var methodBase = memberInfo as MethodBase;
            if (methodBase != null)
                return memberInfos.Cast<MethodBase>().Select(methodBase.GetParameterTypes());

            return memberInfos.Single();
        }
        private static Type Transform(Type type) {

            if (type == typeof(IKingQueryable))
                return typeof(IQueryable);

            if (type == typeof(IKingOrderedQueryable))
                return typeof(IOrderedQueryable);

            if (!type.IsGenericType)
                return type;

            var genericArguments = type.GetGenericArguments().Select(o => Transform(o)).ToArray();
            var genericTypeDefinition = type.GetGenericTypeDefinition();

            if (genericTypeDefinition == typeof(KingTable<>))
                return typeof(Table<>).MakeGenericType(genericArguments[0]);

            if (genericTypeDefinition == typeof(IKingQueryable<>))
                return typeof(IQueryable<>).MakeGenericType(genericArguments[0]);

            if (genericTypeDefinition == typeof(IKingOrderedQueryable<>))
                return typeof(IOrderedQueryable<>).MakeGenericType(genericArguments[0]);

            return genericTypeDefinition.MakeGenericType(genericArguments);
        }
        private static ModuleBuilder ModuleBuilder {
            get {
                if (s_moduleBuilder == null) {
                    var name = "King.Link.Dynamic";
                    var assemblyName = new AssemblyName(name);
                    var assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run);
                    s_moduleBuilder = assemblyBuilder.DefineDynamicModule(name);
                }

                return s_moduleBuilder;
            }
        }
        private static ConstructorInfo MakeAnonymousType(
            IEnumerable<MemberInfo> members, out IEnumerable<MemberInfo> newMembers) {

            var anonymousType = new AnonymousType(members);
            AnonymousType cachedResult;
            if (m_anonymousTypes.TryGetValue(anonymousType, out cachedResult)) {
                newMembers = cachedResult.Members;
                return cachedResult.Constructor;
            }

            var types = new List<Type>();
            var orderAttributeCtor = typeof(OrderAttribute).Get<ConstructorInfo>().First();
            int order = 0;

            var typeBuilder = ModuleBuilder.DefineType("<>king__AnonymousType" + s_anonymousTypeId++);
            foreach (var member in members) {
                var fieldInfo = member as FieldInfo;
                var propertyInfo = member as PropertyInfo;
                var methodInfo = member as MethodInfo;

                var orderAttribute = new CustomAttributeBuilder(orderAttributeCtor, new object[] { order });

                if (fieldInfo != null) {
                    var name = fieldInfo.Name;
                    var type = fieldInfo.FieldType;
                    types.Add(type);
                    var fieldBuilder = typeBuilder.DefineField(name, type, fieldInfo.Attributes);
                    fieldBuilder.SetCustomAttribute(orderAttribute);

                } else if (propertyInfo != null) {
                    var name = propertyInfo.Name;
                    var type = propertyInfo.PropertyType;
                    types.Add(type);

                    var propertyBuilder = typeBuilder.DefineProperty(name, propertyInfo.Attributes, type, Type.EmptyTypes);
                    propertyBuilder.SetCustomAttribute(orderAttribute);

                    var getMethod = typeBuilder.DefineMethod("get_" + name, MethodAttributes.Public, type, Type.EmptyTypes);
                    getMethod.GetILGenerator().ThrowException(typeof(NotImplementedException));

                    var setMethod = typeBuilder.DefineMethod("set_" + name, MethodAttributes.Public, null, new Type[] { type });
                    setMethod.GetILGenerator().ThrowException(typeof(NotImplementedException));

                    propertyBuilder.SetGetMethod(getMethod);
                    propertyBuilder.SetSetMethod(setMethod);

                } else {
                    var name = methodInfo.Name;
                    var type = methodInfo.ReturnType;
                    types.Add(type);

                    var getMethod = typeBuilder.DefineMethod(name, MethodAttributes.Public, type, Type.EmptyTypes);
                    getMethod.SetCustomAttribute(orderAttribute);
                    getMethod.GetILGenerator().ThrowException(typeof(NotImplementedException));
                }
            }

            var ctor = typeBuilder.DefineConstructor(MethodAttributes.Public, CallingConventions.Any, types.ToArray());
            ctor.GetILGenerator().ThrowException(typeof(NotImplementedException));

            var result = typeBuilder.CreateType().Get<ConstructorInfo>().First();
            newMembers =
               (from o in result.DeclaringType.Get<MemberInfo>()
                where o.IsDefined<OrderAttribute>()
                orderby o.GetAttribute<OrderAttribute>().Order
                select o).ToArray();

            anonymousType.Constructor = result;
            anonymousType.Members = newMembers;
            m_anonymousTypes[anonymousType] = anonymousType;

            return result;
        }
        #endregion

        #region Static Private Data members
        private static ModuleBuilder s_moduleBuilder;
        private static long s_anonymousTypeId;
        private static Dictionary<AnonymousType, AnonymousType> m_anonymousTypes;
        private static Dictionary<Type, Type> m_typeTransforms;
        private static Dictionary<MethodInfo, MethodInfo> s_methodTransforms;
        private static Dictionary<Type, PropertyInfo> s_getTable;
        private static MethodInfo s_where, s_intersect, s_union, s_isNullOrEmpty;
        #endregion

        #region Static Constructor
        static KingExpressionTransform() {
            m_anonymousTypes = new Dictionary<AnonymousType, AnonymousType>();
            m_typeTransforms = new Dictionary<Type, Type>();
            s_methodTransforms =
               (from o in typeof(KingQueryable).Get<MethodInfo>(isStatic: true, isPublic: true)
                where typeof(IKingQueryable).IsAssignableFrom(o.GetParameters()[0].ParameterType)
                let methods =
                    from MethodInfo m in typeof(Queryable).GetMember(o.Name)
                    where m.GetGenericArgumentsOrEmpty().Count() == o.GetGenericArgumentsOrEmpty().Count()
                    select m.MakeGenericMethodOrMethod(o.GetGenericArgumentsOrEmpty())
                let parameters =
                    from p in o.GetParameters()
                    select Transform(p.ParameterType)
                select new {
                    Key = o,
                    Value = methods.Select(parameters).GetGenericMethodDefinitionOrMethod()
                }).ToDictionary(o => o.Key, o => o.Value);
            s_getTable = new Dictionary<Type, PropertyInfo>();


            s_where =
               ((Func<IKingQueryable<object>, Expression<Func<object, bool>>, IKingQueryable<object>>)
               KingQueryable.Where).Method.GetGenericMethodDefinition();

            s_intersect =
               ((Func<IKingQueryable<object>, IEnumerable<object>, IKingQueryable<object>>)
               KingQueryable.Intersect).Method.GetGenericMethodDefinition();

            s_union =
               ((Func<IKingQueryable<object>, IEnumerable<object>, IKingQueryable<object>>)
               KingQueryable.Union).Method.GetGenericMethodDefinition();

            s_isNullOrEmpty =
               ((Func<string, bool>)string.IsNullOrEmpty).Method;
        }
        #endregion

        #region Private Data Members
        private Dictionary<ParameterExpression, ParameterExpression> m_parameterExpressions;
        private Stack<Frame> m_stack;
        #endregion

        #region Constructor
        internal KingExpressionTransform() {
            m_stack = new Stack<Frame>();
            m_parameterExpressions = new Dictionary<ParameterExpression, ParameterExpression>();
        }
        #endregion

        #region Private Members
        private PropertyInfo GetTable(Type table) {
            var bf = BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public;
            return s_getTable.GetOrCacheValue(table, o => o.GetProperty("Table", bf));
        }
        private Frame CurrentFrame {
            get {
                if (m_stack.Count == 0)
                    return null;
                return m_stack.Peek();
            }
        }
        private Expression Transform(MethodCallExpression e) {

            // KUDOS: 2009/25/2 Expression tree transforms: Intersect, Union, IsNullOrEmpty, etc
            // source.Intersect(set) => source.Where(o => false || o == s1 || o == s2 || ... || o == sn)
            if (e.Method.IsGenericMethodOf(s_intersect) && e.Arguments[1] is ConstantExpression) {
                e = Expression.Call(null, s_where.MakeGenericMethod(e.Method.GetGenericArguments()[0]),
                    e.Arguments[0], PredicateBuilder.Or(e.Arguments[1]));
            }

            // source.Union(set) => source.Where(o => true && o == s1 && o == s2 && ... && o == sn)
            if (e.Method.IsGenericMethodOf(s_union) && e.Arguments[1] is ConstantExpression) {
                e = Expression.Call(null, s_where.MakeGenericMethod(e.Method.GetGenericArguments()[0]),
                    e.Arguments[0], PredicateBuilder.And(e.Arguments[1]));
            }

            // string.IsNullOrEmpty(target) => target == null || target == ""
            if (e.Method == s_isNullOrEmpty) {
                return Expression.Or(
                    Expression.Equal(Transform(e.Arguments[0]), Expression.Constant(null)),
                    Expression.Equal(Transform(e.Arguments[0]), Expression.Constant(string.Empty)));
            }

            var isLinqMethod = s_methodTransforms.ContainsKey(e.Method.GetGenericMethodDefinitionOrMethod());
            var isSubExpression = typeof(IKingQueryable).IsAssignableFrom(e.Method.ReturnType);
            var isCompilerGenerated = e.Method.DeclaringType.IsDefined<CompilerGeneratedAttribute>();

            var context = Transform(e.Object);
            var method = Transform<MethodInfo>(e.Method);
            var arguments = e.Arguments.Select(o => Transform(o)).ToArray();
            var result = Expression.Call(context, method, arguments);
            if (isLinqMethod || !isSubExpression)
                return result;

            m_stack.Push(new Frame(result));
            var subExpression = Transform(CurrentFrame.Expression);
            m_stack.Pop();
            return subExpression;
        }
        private Expression Transform(LambdaExpression e) {
            var type = Transform(e.Type);
            var body = Transform(e.Body);
            var arguments = e.Parameters.Select(o => Transform(o));
            return Expression.Lambda(type, body, arguments);
        }
        private Expression Transform(MemberExpression e) {
            if (!typeof(IKingQueryable).IsAssignableFrom(e.Type) ||
                e.Member.IsDefined<ColumnAttribute>() ||
                e.Member.IsDefined<AssociationAttribute>())
                return Expression.MakeMemberAccess(Transform(e.Expression), Transform(e.Member));

            if (typeof(KingTable).IsAssignableFrom(e.Type))
                return Expression.Property(e, GetTable(e.Type));

            var isCompilerGenerated = e.Member.DeclaringType.IsDefined<CompilerGeneratedAttribute>();
            var member = e.Member.DeclaringType.Get<MethodInfo>("get_" + e.Member.Name).FirstOrDefault();
            if (!isCompilerGenerated && member != null)
                return Transform(Expression.Call(e.Expression, member));

            return Expression.MakeMemberAccess(Transform(e.Expression), Transform(e.Member));
        }
        private Expression Transform(MemberInitExpression e) {
            return Expression.MemberInit(Transform(e.NewExpression), e.Bindings.Select(o => Transform(o)));
        }
        private MemberBinding Transform(MemberAssignment e) {
            return Expression.Bind(e.Member, Transform(e.Expression));
        }
        private MemberBinding Transform(MemberListBinding e) {
            throw new NotImplementedException();
        }
        private MemberBinding Transform(MemberMemberBinding e) {
            throw new NotImplementedException();
        }
        private Expression Transform(ConstantExpression e) {
            if (e.Type.IsGenericType && e.Type.GetGenericTypeDefinition() == typeof(KingTable<>))
                return Expression.Constant(((KingTable)e.Value).Table);

            if (CurrentFrame != null && e.Value == CurrentFrame.This)
                return CurrentFrame.ThisExpression;

            // TODO: Transform parmeters too.

            return e;
        }
        private NewExpression Transform(NewExpression e) {
            var constructor = Transform<ConstructorInfo>(e.Constructor);
            var arguments = e.Arguments.Select(o => Transform(o)).ToArray();
            if (e.Members == null)
                return Expression.New(constructor, arguments);
            var members = e.Members.Select(o => Transform(o)).ToArray();
            return Expression.New(constructor, arguments, members);
        }
        private Expression Transform(InvocationExpression e) {
            return Expression.Invoke(Transform(e.Expression), e.Arguments.Select(o => Transform(o)));
        }
        private Expression Transform(BinaryExpression e) {
            return Expression.MakeBinary(e.NodeType, Transform(e.Left), Transform(e.Right), e.IsLiftedToNull, e.Method);
        }
        private Expression Transform(UnaryExpression e) {
            return Expression.MakeUnary(e.NodeType, Transform(e.Operand), Transform(e.Type), e.Method);
        }
        private Expression Transform(ConditionalExpression e) {
            return Expression.Condition(Transform(e.Test), Transform(e.IfTrue), Transform(e.IfFalse));
        }
        private ParameterExpression Transform(ParameterExpression e) {
            ParameterExpression cached;
            if (m_parameterExpressions.TryGetValue(e, out cached))
                return cached;

            cached = Expression.Parameter(Transform(e.Type), e.Name);
            m_parameterExpressions[e] = cached;
            return cached;
        }
        private Expression Transform(NewArrayExpression e) {
            return Expression.NewArrayInit(Transform(e.Type.GetElementType()), e.Expressions.Select(o => Transform(o)));
        }
        private Expression Transform(TypeBinaryExpression e) {
            throw new NotImplementedException();
        }
        #endregion

        #region Internal Members
        [DebuggerHidden]
        private MemberBinding Transform(MemberBinding e) {
            if (e == null)
                return null;
            if (e is MemberAssignment)
                return Transform((MemberAssignment)e);
            if (e is MemberListBinding)
                return Transform((MemberListBinding)e);
            if (e is MemberMemberBinding)
                return Transform((MemberMemberBinding)e);
            throw new NotImplementedException("Transform frame is marked as: [DebuggerHidden]");
        }
        [DebuggerHidden]
        internal Expression Transform(Expression e) {
            if (e == null)
                return null;
            if (e is MethodCallExpression)
                return Transform((MethodCallExpression)e);
            if (e is BinaryExpression)
                return Transform((BinaryExpression)e);
            if (e is UnaryExpression)
                return Transform((UnaryExpression)e);
            if (e is LambdaExpression)
                return Transform((LambdaExpression)e);
            if (e is ConstantExpression)
                return Transform((ConstantExpression)e);
            if (e is MemberExpression)
                return Transform((MemberExpression)e);
            if (e is NewArrayExpression)
                return Transform((NewArrayExpression)e);
            if (e is ParameterExpression)
                return Transform((ParameterExpression)e);
            if (e is TypeBinaryExpression)
                return Transform((TypeBinaryExpression)e);
            if (e is NewExpression)
                return Transform((NewExpression)e);
            if (e is MemberInitExpression)
                return Transform((MemberInitExpression)e);
            if (e is ConditionalExpression)
                return Transform((ConditionalExpression)e);
            if (e is InvocationExpression)
                return Transform((InvocationExpression)e);
            throw new NotImplementedException("Transform frame is marked as: [DebuggerHidden]");
        }
        #endregion
    }

    public delegate void OnExecuteDelegate(IKingQueryable queryable);
    #endregion
}

